using AdminApiTemplate.Common.Attributes;
using AdminApiTemplate.Common.Exceptions;
using System.ComponentModel;
using System.Reflection;

namespace AdminApiTemplate.Common.Helpers
{
    public class ParameterHelper
    {
        public static void AddIf(List<SqlParameterItem> ps, bool whereExp, string key, object val, OperationTypeEnum operatingType = OperationTypeEnum.Equal)
        {
            if (whereExp)
            {
                ps.Add(new SqlParameterItem
                {
                    Key = key,
                    OperatingType = operatingType,
                    Val = val
                });
            }
        }

        public static List<SqlParameterItem> Build<T>(T request)
        {
            var ps = new List<SqlParameterItem>();
            var ttype = typeof(T);
            var tps = ttype.GetProperties();
            foreach (var tp in tps)
            {
                var val = tp.GetValue(request, null);
                if (val == null)
                {
                    continue;
                }

                var val_display = Convert.ToString(val);
                if (string.IsNullOrEmpty(val_display))
                {
                    continue;
                }

                var addIfAttr = tp.GetCustomAttribute<AddIfAttribute>();
                if (addIfAttr != null)
                {
                    var tpName = tp.Name;
                    if (!string.IsNullOrEmpty(addIfAttr.Field))
                    {
                        tpName = addIfAttr.Field;
                    }

                    if (addIfAttr.OperationType == AddIfOperationTypeEnum.Range)
                    {
                        var parts = val_display.Split(new char[] { ',' });
                        if (parts.Length != 2)
                        {
                            throw new WithCodeException((int)ErrorCodeEnum.InvalidArguments, $"无效的参数{tp.Name}:{val_display}");
                        }

                        var val1 = ConvertHelper.GetVal(addIfAttr.FieldType.ToString(), parts[0]);
                        var val2 = ConvertHelper.GetVal(addIfAttr.FieldType.ToString(), parts[1]);
                        if (addIfAttr.FieldType == AddIfFieldTypeEnum.DateTime && addIfAttr.AddDays)
                        {
                            var val3 = (DateTime)val2;
                            val2 = val3.AddDays(1).Date;
                        }
                        AddIf(ps, val1 != null, tpName, val1, OperationTypeEnum.GreaterEqual);
                        AddIf(ps, val2 != null, tpName, val2, OperationTypeEnum.Less);
                    }
                    else if (addIfAttr.OperationType == AddIfOperationTypeEnum.In)
                    {
                        var parts = val_display.Split(new char[] { ',' });
                        AddIf(ps, true, $" and `{tpName}` in ('{string.Format("','", parts)}')", null, OperationTypeEnum.Format);
                    }
                    else if (addIfAttr.OperationType == AddIfOperationTypeEnum.SqlFormat)
                    {
                        var val_real = GetVal(tp, val_display);
                        AddIf(ps, true, val_real.ToString(), null, OperationTypeEnum.Format);
                    }
                    else
                    {
                        var val_real = GetVal(tp, val_display);
                        AddIf(ps, true, tpName, val_real, ConvertToOperationType(addIfAttr.OperationType));
                    }
                }
            }
            return ps;
        }

        public static object GetVal(PropertyInfo p, string val)
        {
            if (p.PropertyType == typeof(int) || p.PropertyType == typeof(int?))
            {
                return ConvertHelper.GetInt(val);
            }

            if (p.PropertyType == typeof(decimal) || p.PropertyType == typeof(decimal?))
            {
                return ConvertHelper.GetDecimal(val);
            }

            if (p.PropertyType == typeof(DateTime) || p.PropertyType == typeof(DateTime?))
            {
                return ConvertHelper.GetDateTime(val);
            }

            return val;
        }

        private static OperationTypeEnum ConvertToOperationType(AddIfOperationTypeEnum operationType)
        {
            switch (operationType)
            {
                default:
                    return OperationTypeEnum.Equal;
                case AddIfOperationTypeEnum.NotEqual:
                    return OperationTypeEnum.NotEqual;
                case AddIfOperationTypeEnum.Like:
                    return OperationTypeEnum.Like;
                case AddIfOperationTypeEnum.LeftLike:
                    return OperationTypeEnum.LeftLike;
                case AddIfOperationTypeEnum.RightLike:
                    return OperationTypeEnum.RightLike;
                case AddIfOperationTypeEnum.Greater:
                    return OperationTypeEnum.Greater;
                case AddIfOperationTypeEnum.GreaterEqual:
                    return OperationTypeEnum.GreaterEqual;
                case AddIfOperationTypeEnum.Less:
                    return OperationTypeEnum.Less;
                case AddIfOperationTypeEnum.LessEqual:
                    return OperationTypeEnum.LessEqual;
            }
        }

        /// <summary>
        /// 构造筛选条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <param name="dict"></param>
        /// <param name="sps"></param>
        /// <returns></returns>
        public static string BuilderFilter<T>(T request, Dictionary<string, object> dict, params SqlParameterItem[] sps)
        {
            var filter = "";
            var ps = Build<T>(request);
            if (sps!=null && sps.Length>0)
            {
                ps.AddRange(sps);
            }

            if (ps.Count > 0)
            {
                var pairs = new List<string>();
                for (var i = 0; i<ps.Count; i++)
                {
                    var sp = ps[i];
                    var key = $"p_{i}";
                    var field = $"`{sp.Key}`";
                    if (sp.Key.IndexOf(".")>=0)
                    {
                        field = sp.Key;
                    }
                    if (sp.OperatingType == OperationTypeEnum.Format)
                    {
                        pairs.Add(string.Format(sp.Key, sp.Val));
                    }
                    else if (sp.OperatingType == OperationTypeEnum.FormatWithParameter)
                    {
                        pairs.Add(string.Format(sp.Key, $"@{key}"));
                        dict[key] = sp.Val;
                    }
                    else if (sp.OperatingType == OperationTypeEnum.LeftLike)
                    {
                        pairs.Add($"{field} like @{key}");
                        dict[key] = $"{sp.Val}%";
                    }
                    else if (sp.OperatingType == OperationTypeEnum.Like)
                    {
                        pairs.Add($"{field} like @{key}");
                        dict[key] = $"%{sp.Val}%";
                    }
                    else if (sp.OperatingType == OperationTypeEnum.RightLike)
                    {
                        pairs.Add($"{field} like @{key}");
                        dict[key] = $"%{sp.Val}";
                    }
                    else
                    {
                        pairs.Add($"{field} {GetOperatingType(sp.OperatingType)} @{key}");
                        dict[key] = sp.Val;
                    }
                }
                filter = $"where {string.Join(" and ", pairs)}";
            }
            return filter;
        }

        private static string GetOperatingType(OperationTypeEnum operatingType)
        {
            switch (operatingType)
            {
                default:
                    throw new WithCodeException((int)ErrorCodeEnum.CodeError, $"不支持的操作类型：{operatingType}");
                case OperationTypeEnum.Equal:
                    return "=";
                case OperationTypeEnum.NotEqual:
                    return "<>";
                case OperationTypeEnum.Greater:
                    return ">";
                case OperationTypeEnum.GreaterEqual:
                    return ">=";
                case OperationTypeEnum.Less:
                    return "<";
                case OperationTypeEnum.LessEqual:
                    return "<=";
            }
        }
    }

    public class SqlParameterItem
    {
        public string Key { get; set; }

        public object Val { get; set; }

        public OperationTypeEnum OperatingType { get; set; }
    }

    public enum OperationTypeEnum
    {
        /// <summary>
        /// 等于
        /// </summary>
        [Description("等于")]
        Equal = 0,

        /// <summary>
        /// 不等于
        /// </summary>
        [Description("不等于")]
        NotEqual = 1,

        /// <summary>
        /// 相似
        /// </summary>
        [Description("相似")]
        Like = 2,

        /// <summary>
        /// 左相似
        /// </summary>
        [Description("左相似")]
        LeftLike = 3,

        /// <summary>
        /// 右相似
        /// </summary>
        [Description("右相似")]
        RightLike = 4,

        /// <summary>
        /// 小于
        /// </summary>
        [Description("小于")]
        Greater = 5,

        /// <summary>
        /// 小于等于
        /// </summary>
        [Description("小于等于")]
        GreaterEqual = 6,

        /// <summary>
        /// 大于
        /// </summary>
        [Description("大于")]
        Less = 7,

        /// <summary>
        /// 大于等于
        /// </summary>
        [Description("大于等于")]
        LessEqual = 8,

        /// <summary>
        /// 格式化
        /// </summary>
        [Description("格式化")]
        Format = 9,

        /// <summary>
        /// 格式化（使用参数）
        /// </summary>
        [Description("格式化（使用参数）")]
        FormatWithParameter = 10,
    }
}
